package com.ctao.dialer;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.telephony.PhoneNumberUtils;
import android.text.Editable;
import android.text.Spanned;
import android.text.method.DialerKeyListener;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.widget.ImageButton;
import android.widget.Toast;

import com.ctao.dialer.widght.DigitsEditText;

import java.util.HashSet;

/**
 * Created by A Miracle on 2016/4/27.
 */
public class MainActivity extends Activity implements OnClickListener, OnLongClickListener, OnTouchListener {

    private static final String TAG = MainActivity.class.getSimpleName();

    private static final char PAUSE = ',';//延长暂停时间2秒
    private static final char WAIT = ';';//延长等待时间

    /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys (流类型用于播放DTMF音调打电话,和映射到音量控制键)*/
    private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;

    /** The DTMF tone volume relative to other sounds in the stream (DTMF语气体积相对于其他的声音流)*/
    private static final int TONE_RELATIVE_VOLUME = 80;

    /** The length of DTMF tones in milliseconds(DTMF音调以毫秒为单位的长度) */
    private static final int TONE_LENGTH_INFINITE = 300;

    // determines if we want to playback local DTMF tones.(确定如果我们想播放本地DTMF音调。)
    private boolean mDTMFToneEnabled;

    /** Set of dialpad keys that are currently being pressed */
    private final HashSet<View> mPressedDialpadKeys = new HashSet<View>(12);

    private ToneGenerator mToneGenerator; //音频发生器
    private final Object mToneGeneratorLock = new Object(); //Lock

    private ImageButton deleteButton;
    private DigitsEditText mDigits;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();

        initListener();
    }

    @Override
    protected void onStart() {
        super.onStart();
        synchronized (mToneGeneratorLock) {
            if (mToneGenerator == null) {
                try {
                    mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
                } catch (RuntimeException e) {
                    Log.w(TAG, "Exception caught while creating local tone generator: " + e);
                    mToneGenerator = null;
                }
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        final ContentResolver contentResolver = getContentResolver();
        mDTMFToneEnabled = Settings.System.getInt(contentResolver,
                Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;

        mPressedDialpadKeys.clear();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mPressedDialpadKeys.clear();
    }

    @Override
    protected void onStop() {
        super.onStop();
        synchronized (mToneGeneratorLock) {
            if (mToneGenerator != null) {
                mToneGenerator.release();
                mToneGenerator = null;
            }
        }
    }

    private void initView() {
        deleteButton = (ImageButton) findViewById(R.id.deleteButton);
        mDigits = (DigitsEditText) findViewById(R.id.digits);
    }

    private void initListener() {
        mDigits.setKeyListener(new DialerKeyListener(){
            @Override
            public CharSequence filter(CharSequence source, int start, int end,
                                       Spanned dest, int dstart, int dend) {
                final String converted = PhoneNumberUtils.convertKeypadLettersToDigits(
                        replaceUnicodeDigits(source.toString()));
                // PhoneNumberUtils.replaceUnicodeDigits performs a character for character replacement,
                // so we can assume that start and end positions should remain unchanged.
                CharSequence result = super.filter(converted, start, end, dest, dstart, dend);
                if (result == null) {
                    if (source.equals(converted)) {
                        // There was no conversion or filtering performed. Just return null according to
                        // the behavior of DialerKeyListener.
                        return null;
                    } else {
                        // filter returns null if the charsequence is to be returned unchanged/unfiltered.
                        // But in this case we do want to return a modified character string (even if
                        // none of the characters in the modified string are filtered). So if
                        // result == null we return the unfiltered but converted numeric string instead.
                        return converted.subSequence(start, end);
                    }
                }
                return result;
            }

            /**
             * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
             *
             * @param number the number to perform the replacement on.
             * @return the replaced number.
             */
            public String replaceUnicodeDigits(String number) {
                StringBuilder normalizedDigits = new StringBuilder(number.length());
                for (char c : number.toCharArray()) {
                    int digit = Character.digit(c, 10);
                    if (digit != -1) {
                        normalizedDigits.append(digit);
                    } else {
                        normalizedDigits.append(c);
                    }
                }
                return normalizedDigits.toString();
            }
        });
        mDigits.setOnClickListener(this);
        mDigits.setOnLongClickListener(this);
        mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());

        findViewById(R.id.dv_1).setOnTouchListener(this);
        findViewById(R.id.dv_2).setOnTouchListener(this);
        findViewById(R.id.dv_3).setOnTouchListener(this);
        findViewById(R.id.dv_4).setOnTouchListener(this);
        findViewById(R.id.dv_5).setOnTouchListener(this);
        findViewById(R.id.dv_6).setOnTouchListener(this);
        findViewById(R.id.dv_7).setOnTouchListener(this);
        findViewById(R.id.dv_8).setOnTouchListener(this);
        findViewById(R.id.dv_9).setOnTouchListener(this);
        findViewById(R.id.dv_xing).setOnTouchListener(this);
        findViewById(R.id.dv_0).setOnTouchListener(this);
        findViewById(R.id.dv_jing).setOnTouchListener(this);

        findViewById(R.id.dv_xing).setOnLongClickListener(this);
        findViewById(R.id.dv_0).setOnLongClickListener(this);
        findViewById(R.id.dv_jing).setOnLongClickListener(this);

        findViewById(R.id.ib_call).setOnClickListener(this);

        deleteButton.setOnClickListener(this);
        deleteButton.setOnLongClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.digits:
                if (mDigits.length() != 0) {
                    mDigits.setCursorVisible(true);
                }
                break;
            case R.id.deleteButton:
                keyPressed(KeyEvent.KEYCODE_DEL);
                break;
            case R.id.ib_call:
                Toast.makeText(getApplicationContext(), mDigits.getText().toString(), Toast.LENGTH_SHORT).show();
                Editable digits = mDigits.getText();
                if(digits != null){
                    digits.clear();
                }
                break;
            default:
                break;
        }
    }

    private void keyPressed(int keyCode) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_1:
                playTone(ToneGenerator.TONE_DTMF_1, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_2:
                playTone(ToneGenerator.TONE_DTMF_2, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_3:
                playTone(ToneGenerator.TONE_DTMF_3, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_4:
                playTone(ToneGenerator.TONE_DTMF_4, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_5:
                playTone(ToneGenerator.TONE_DTMF_5, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_6:
                playTone(ToneGenerator.TONE_DTMF_6, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_7:
                playTone(ToneGenerator.TONE_DTMF_7, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_8:
                playTone(ToneGenerator.TONE_DTMF_8, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_9:
                playTone(ToneGenerator.TONE_DTMF_9, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_0:
                playTone(ToneGenerator.TONE_DTMF_0, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_POUND:
                playTone(ToneGenerator.TONE_DTMF_P, TONE_LENGTH_INFINITE);
                break;
            case KeyEvent.KEYCODE_STAR:
                playTone(ToneGenerator.TONE_DTMF_S, TONE_LENGTH_INFINITE);
                break;
            default:
                break;
        }

        // 振动 mHaptic.vibrate();
        KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
        mDigits.onKeyDown(keyCode, event);

        // 如果光标在文本的最后我们隐藏它。
        final int length = mDigits.length();
        if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) {
            mDigits.setCursorVisible(false);
        }
    }

    /**
     * Play the specified tone for the specified milliseconds
     *
     * The tone is played locally, using the audio stream for phone calls.
     * Tones are played only if the "Audible touch tones" user preference
     * is checked, and are NOT played if the device is in silent mode.
     *
     * The tone length can be -1, meaning "keep playing the tone." If the caller does so, it should
     * call stopTone() afterward.
     *
     * @param tone a tone code from {@link ToneGenerator}
     * @param durationMs tone length.
     */
    private void playTone(int tone, int durationMs) {
        // if local tone playback is disabled, just return.
        if (!mDTMFToneEnabled) {
            return;
        }

        // Also do nothing if the phone is in silent mode.
        // We need to re-check the ringer mode for *every* playTone()
        // call, rather than keeping a local flag that's updated in
        // onResume(), since it's possible to toggle silent mode without
        // leaving the current activity (via the ENDCALL-longpress menu.)
        AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        int ringerMode = audioManager.getRingerMode();
        if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
                || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
            return;
        }

        synchronized (mToneGeneratorLock) {
            if (mToneGenerator == null) {
                Log.w(TAG, "playTone: mToneGenerator == null, tone: " + tone);
                return;
            }

            // Start the new tone (will stop any playing tone)
            mToneGenerator.startTone(tone, durationMs);
        }
    }

    /**
     * Stop the tone if it is played.
     */
    private void stopTone() {
        // if local tone playback is disabled, just return.
        if (!mDTMFToneEnabled) {
            return;
        }
        synchronized (mToneGeneratorLock) {
            if (mToneGenerator == null) {
                Log.w(TAG, "stopTone: mToneGenerator == null");
                return;
            }
            mToneGenerator.stopTone();
        }
    }

    @Override
    public boolean onLongClick(View v) {
        switch (v.getId()) {
            case R.id.digits:
                if(mDigits.length() != 0){
                    mDigits.setCursorVisible(true);
                }
                break;
            case R.id.deleteButton:
                Editable digits = mDigits.getText();
                if(digits != null){
                    digits.clear();
                }
                return true;
            case R.id.dv_0:
                // Remove tentative input ('0') done by onTouch().
                removePreviousDigitIfPossible();

                keyPressed(KeyEvent.KEYCODE_PLUS);

                // Stop tone immediately
                stopTone();
                mPressedDialpadKeys.remove(v);
                return true;
            case R.id.dv_xing:
                // Remove tentative input ('*') done by onTouch().
                removePreviousDigitIfPossible();

                updateDialString(PAUSE);

                // Stop tone immediately
                stopTone();
                mPressedDialpadKeys.remove(v);
                return true;
            case R.id.dv_jing:
                // Remove tentative input ('#') done by onTouch().
                removePreviousDigitIfPossible();

                updateDialString(WAIT);

                // Stop tone immediately
                stopTone();
                mPressedDialpadKeys.remove(v);
                return true;
        }
        return false;
    }

    /**
     * Remove the digit just before the current position. This can be used if we want to replace
     * the previous digit or cancel previously entered character.
     */
    private void removePreviousDigitIfPossible() {
        final int currentPosition = mDigits.getSelectionStart();
        if (currentPosition > 0) {
            mDigits.setSelection(currentPosition);
            mDigits.getText().delete(currentPosition - 1, currentPosition);
        }
    }

    /**
     * Updates the dial string (mDigits) after inserting a Pause character (,)
     * or Wait character (;).
     */
    private void updateDialString(char newDigit) {
        if (newDigit != WAIT && newDigit != PAUSE) {
            throw new IllegalArgumentException(
                    "Not expected for anything other than PAUSE & WAIT");
        }

        int selectionStart;
        int selectionEnd;

        // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
        int anchor = mDigits.getSelectionStart();
        int point = mDigits.getSelectionEnd();

        selectionStart = Math.min(anchor, point);
        selectionEnd = Math.max(anchor, point);

        if (selectionStart == -1) {
            selectionStart = selectionEnd = mDigits.length();
        }

        Editable digits = mDigits.getText();

        if (canAddDigit(digits, selectionStart, selectionEnd, newDigit)) {
            digits.replace(selectionStart, selectionEnd, Character.toString(newDigit));

            if (selectionStart != selectionEnd) {
                // Unselect: back to a regular cursor, just pass the character inserted.
                mDigits.setSelection(selectionStart + 1);
            }
        }
    }

    /**
     * Returns true of the newDigit parameter can be added at the current selection
     * point, otherwise returns false.
     * Only prevents input of WAIT and PAUSE digits at an unsupported position.
     * Fails early if start == -1 or start is larger than end.
     */
    @VisibleForTesting
    /* package */ static boolean canAddDigit(CharSequence digits, int start, int end,
                                             char newDigit) {
        if(newDigit != WAIT && newDigit != PAUSE) {
            throw new IllegalArgumentException(
                    "Should not be called for anything other than PAUSE & WAIT");
        }

        // False if no selection, or selection is reversed (end < start)
        if (start == -1 || end < start) {
            return false;
        }

        // unsupported selection-out-of-bounds state
        if (start > digits.length() || end > digits.length()) return false;

        // Special digit cannot be the first digit
        if (start == 0) return false;

        if (newDigit == WAIT) {
            // preceding char is ';' (WAIT)
            if (digits.charAt(start - 1) == WAIT) return false;

            // next char is ';' (WAIT)
            if ((digits.length() > end) && (digits.charAt(end) == WAIT)) return false;
        }

        return true;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                switch (v.getId()) {
                    case R.id.dv_1:
                        keyPressed(KeyEvent.KEYCODE_1);
                        break;
                    case R.id.dv_2:
                        keyPressed(KeyEvent.KEYCODE_2);
                        break;
                    case R.id.dv_3:
                        keyPressed(KeyEvent.KEYCODE_3);
                        break;
                    case R.id.dv_4:
                        keyPressed(KeyEvent.KEYCODE_4);
                        break;
                    case R.id.dv_5:
                        keyPressed(KeyEvent.KEYCODE_5);
                        break;
                    case R.id.dv_6:
                        keyPressed(KeyEvent.KEYCODE_6);
                        break;
                    case R.id.dv_7:
                        keyPressed(KeyEvent.KEYCODE_7);
                        break;
                    case R.id.dv_8:
                        keyPressed(KeyEvent.KEYCODE_8);
                        break;
                    case R.id.dv_9:
                        keyPressed(KeyEvent.KEYCODE_9);
                        break;
                    case R.id.dv_0:
                        keyPressed(KeyEvent.KEYCODE_0);
                        break;
                    case R.id.dv_xing:
                        keyPressed(KeyEvent.KEYCODE_STAR);
                        break;
                    case R.id.dv_jing:
                        keyPressed(KeyEvent.KEYCODE_POUND);
                        break;
                    default:
                        break;
                }
                mPressedDialpadKeys.add(v);

            }
            break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                mPressedDialpadKeys.remove(v);
                if (mPressedDialpadKeys.isEmpty()) {
                    stopTone();
                }
                break;
            default:
                break;
        }
        return false;
    }
}
